#ifndef XG_OPENSSL_RSAENTITY_CPP
#define XG_OPENSSL_RSAENTITY_CPP
/////////////////////////////////////////////////////////////////
#include "../RSAEntity.h"

#define RSAENTITY_BUFFER_MAXSIZE 64 * 1024

string SHAEncode(const string& msg)
{
	SHA_CTX ctx;
	char buffer[SHA_DIGEST_LENGTH + 1];

	if (SHA1_Init(&ctx) == 0) return stdx::EmptyString();

	SHA1_Update(&ctx, (u_char*)(msg.c_str()), msg.length());
	SHA1_Final((u_char*)(buffer), &ctx);
	OPENSSL_cleanse(&ctx, sizeof(ctx));

	return string(buffer, SHA_DIGEST_LENGTH);
}

RSAEntity::RSAEntity()
{
	pubkey = NULL;
	prikey = NULL;
	crypted = true;
	padding = RSA_PKCS1_PADDING;
}
RSAEntity::~RSAEntity()
{
	close();
}
void RSAEntity::close()
{
	if (pubkey)
	{
		RSA_free(pubkey);
		pubkey = NULL;
	}

	if (prikey)
	{
		RSA_free(prikey);
		prikey = NULL;
	}
}
void RSAEntity::setPadding(int padding)
{
	this->padding = padding;
}
void RSAEntity::setCrypted(bool crypted)
{
	this->crypted = crypted;
}
bool RSAEntity::init(const char* pubkeypath, const char* prikeypath)
{
	FILE* fp = NULL;

	close();

	if (pubkeypath)
	{
		if ((fp = fopen(stdx::syscode(pubkeypath).c_str(), "r")) == NULL) return false;

		if ((pubkey = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL)) == NULL)
		{
			fclose(fp);

			return false;
		}

		fclose(fp);

		if (RSA_size(pubkey) >= RSAENTITY_BUFFER_MAXSIZE) return false;
	}

	if (prikeypath)
	{
		if ((fp = fopen(stdx::syscode(prikeypath).c_str(), "r")) == NULL) return false;

		if ((prikey = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL)) == NULL)
		{
			fclose(fp);

			return false;
		}

		fclose(fp);

		if (RSA_size(prikey) >= RSAENTITY_BUFFER_MAXSIZE) return false;
	}

	return true;
}
string RSAEntity::encode(const string& msg)
{
	int len = 0;
	char buffer[RSAENTITY_BUFFER_MAXSIZE];

	if (crypted)
	{
		if ((len = RSA_public_encrypt(msg.length(), (u_char*)(msg.c_str()), (u_char*)(buffer), pubkey, padding)) < 0)
		{
			return stdx::EmptyString();
		}
	}
	else
	{
		if ((len = RSA_private_encrypt(msg.length(), (u_char*)(msg.c_str()), (u_char*)(buffer), prikey, padding)) < 0)
		{
			return stdx::EmptyString();
		}
	}

	return string(buffer, len);
}
string RSAEntity::decode(const string& msg)
{
	int len = 0;
	char buffer[RSAENTITY_BUFFER_MAXSIZE];

	if (crypted)
	{
		if ((len = RSA_private_decrypt(msg.length(), (u_char*)(msg.c_str()), (u_char*)(buffer), prikey, padding)) < 0)
		{
			return stdx::EmptyString();
		}
	}
	else
	{
		if ((len = RSA_public_decrypt(msg.length(), (u_char*)(msg.c_str()), (u_char*)(buffer), pubkey, padding)) < 0)
		{
			return stdx::EmptyString();
		}
	}

	return string(buffer, len);
}
string RSAEntity::sign(const string& msg)
{
	u_int len = RSA_size(prikey);
	string tag = SHAEncode(msg);
	char buffer[RSAENTITY_BUFFER_MAXSIZE];

	return RSA_sign(NID_sha1, (u_char*)(tag.c_str()), tag.length(), (u_char*)(buffer), &len, prikey) == 1 ? string(buffer, len) : stdx::EmptyString();
}
bool RSAEntity::verify(const string& msg, const string& sign)
{
	string tag = SHAEncode(msg);

	return RSA_verify(NID_sha1, (u_char*)(tag.c_str()), tag.length(), (u_char*)(sign.c_str()), sign.length(), pubkey) == 1;
}
/////////////////////////////////////////////////////////////////
#endif